
#ifndef __SMPIO_H__
#define __SMPIO_H__

#pragma warning( push )
#pragma warning(disable : 4996)

//
// define TRACE
//
#ifdef __cplusplus
#define SMPIO_TRACE(io, exp) {(io)->PreTrace(__TFILE__, __LINE__); (io)->Trace exp;}
#ifndef TRACE
#define TRACE g_SmpIO.PreTrace(__TFILE__, __LINE__); g_SmpIO.Trace
#endif	//#ifndef TRACE
#else	//#ifdef __cplusplus
#ifndef TRACE
#define TRACE SmpIOPrintf
#endif	//#ifndef TRACE
#endif	//#ifdef __cplusplus



#ifdef __cplusplus
extern "C" {
#endif	//#ifdef __cplusplus

int SmpIOPrintf(const TCHAR* format ...);
int SmpIODeepPrintf(DWORD dwDeep, const TCHAR* format ...);
int SmpIOPuts(const TCHAR* str);
int SmpIOWrite(BYTE* pbuf, DWORD dwLen);
int SmpIOTRACE(DWORD dwDeep, const TCHAR* pszFile, int iLine, const TCHAR* format ...);

#ifdef __cplusplus
}
#endif	//#ifdef __cplusplus

#ifdef __cplusplus

#include "MiscUtil.h"

#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

class ISmpIO
{
public:
	ISmpIO()
		:m_szFile(NULL), m_dwDeep(0)
	{
		InitializeCriticalSection(&m_cs);
	};

	virtual ~ISmpIO()
	{
		DeleteCriticalSection(&m_cs);
	};

	virtual BOOL Init(const TCHAR* szInit)
		{return TRUE;};

public:
	virtual int Printf(const TCHAR* format ...);
	virtual int Printf(DWORD dwDeep, const TCHAR* format ...);
	virtual int Puts(const TCHAR* str){return 0;};
	virtual int Write(BYTE* pbuf, DWORD dwLen){return 0;};


public:
//use SMPIOTRACE to call trace function
	virtual void PreTrace(const TCHAR* szFile, DWORD dwLine){m_szFile=szFile; m_dwLine=dwLine;};
	virtual void Trace(const TCHAR* format ...);
	virtual void Trace(DWORD dwDeep, const TCHAR* format ...);

public:
	virtual int Scanf(const TCHAR* format ... );
	virtual int Gets(TCHAR* psz, DWORD dwLen){return 0;};
	virtual int Read(BYTE* pbuf, DWORD dwLen){return 0;};

public:
	DWORD GetDeep(){return m_dwDeep;}
	DWORD SetDeep(DWORD dwDeep)
	{
		DWORD ret=m_dwDeep;
		m_dwDeep=dwDeep;
		return ret;
	}
	VOID IncDeep(){m_dwDeep++;};
	VOID DecDeep(){if(0!=m_dwDeep){m_dwDeep--;};};

//protected:
public:
	virtual int _input (const TCHAR* format, va_list arglist ){return 0;};

//protected:
public:
	int PrintfV(DWORD dwDeep, const TCHAR* pFile, DWORD dwLine, const TCHAR* format, va_list arglist);
protected:
	VOID ReFormat(DWORD dwDeep, const TCHAR* pFile, DWORD dwLine, const TCHAR* format, TCHAR* reformat);

protected:
	DWORD m_dwDeep;
	CRITICAL_SECTION	m_cs;

protected:
	const TCHAR* m_szFile;
	DWORD m_dwLine;

};

/*
#ifdef _WINCE_
extern "C" int __cdecl _input(__in FILE * _File, __in_z __format_string const TCHAR * _Format, va_list _ArgList);
#else
extern "C" int __cdecl _input_l(__inout FILE * _File, __in_z __format_string const unsigned char *, __in_opt _locale_t _Locale, va_list _ArgList);
extern "C" int __cdecl _winput_l(__inout FILE * _File, __in_z __format_string const wchar_t *, __in_opt _locale_t _Locale, va_list _ArgList);
#endif

#define _tinput_l        _winput_l
*/
//
// register console if needed
class CConsoleIO
	:public ISmpIO
{
public:
	virtual ~CConsoleIO(){};

public:
	int Puts(const TCHAR* str){return _tprintf(str);};

public:
	int Gets(TCHAR* psz, DWORD dwSize){ return _getts(psz)?(int)_tcslen(psz):0;};
	virtual int Read(BYTE* pbuf, DWORD dwLen){return 0;};

private:
//	int _input (const TCHAR* format, va_list arglist ){return ::_tinput_l(stdin, format, NULL, arglist);};

};


class CDbgoutIO
	:public ISmpIO
{
public:
	virtual ~CDbgoutIO(){};

public:
	int Puts(const TCHAR* str){OutputDebugString(str); return (int)_tcslen(str);};
};

class CAtlIO
	:public ISmpIO
{
public:
	virtual ~CAtlIO(){};
	
};

class CSocketIO
	:public ISmpIO
{
public:
	virtual ~CSocketIO(){};
		
};


class CWindowIO
	:public ISmpIO
{
public:
	virtual ~CWindowIO(){};
		
};

class CCelogIO
	:public ISmpIO
{
public:
	virtual ~CCelogIO(){};
		
};

class CSerialIO
	:public ISmpIO
{
public:
	virtual ~CSerialIO(){};
		
};

class CFileIO
	:public ISmpIO
{
public:
	virtual ~CFileIO(){};

	BOOL Init(const TCHAR* szInit);
public:
	int Puts(const TCHAR* str);
	int Write(BYTE* pbuf, DWORD dwLen);
		
public:
	int Gets(TCHAR* psz, DWORD dwLen);
	int Read(BYTE* pbuf, DWORD dwLen);

private:
	HANDLE m_hFile;
};


enum SMPIO_CLASS {SMPIO_CONSOLE, SMPIO_DBGOUT, SMPIO_ATL, SMPIO_SOCKET, SMPIO_WINDOWS
					, SMPIO_CELOG, SMPIO_SERIAL, SMPIO_FILE};
enum SMPIO_TYPE {SMIO_TYPE_INOUT, SMIO_TYPE_IN, SMIO_TYPE_OUT};

class CSmpIO
	:public ISmpIO
{
public:
	CSmpIO(){Init();};
	CSmpIO(SMPIO_CLASS type);
	virtual ~CSmpIO(){};
private:
	VOID Init();

public:
	int Puts(const TCHAR* str);
	int Write(BYTE* pbuf, DWORD dwLen);

public:
	int Gets(TCHAR* psz, DWORD dwLen);
	int Read(BYTE* pbuf, DWORD dwLen);
private:
	int _input (const TCHAR* format, va_list arglist );

public:
	void RegisterInput(ISmpIO* pIO){m_vInIO.push_back(pIO);};
	void UnregisterInput(ISmpIO* pIO){Unregister(&m_vInIO, pIO);};
	void RegisterOutput(ISmpIO* pIO){m_vOutIO.push_back(pIO);};
	void UnregisterOutput(ISmpIO* pIO){Unregister(&m_vOutIO, pIO);};
	void RegisterIO(ISmpIO* pIO){m_vInIO.push_back(pIO); m_vOutIO.push_back(pIO);};
	void UnregisterIO(ISmpIO* pIO){Unregister(&m_vInIO, pIO); Unregister(&m_vOutIO, pIO);};

public:
	BOOL EnBuildInIO(BOOL fEnable, SMPIO_CLASS smpiocls, SMPIO_TYPE smpiotype, const TCHAR* szInit=NULL);
private:
	template <class T>
	BOOL EnBuildInIO(BOOL fEnable, T* pt, SMPIO_TYPE smpiotype, const TCHAR* szInit)
	{
		if( ((fEnable) && (pt))
			|| ((!fEnable) && (!pt)) )
		{
			return TRUE;
		}

		if(fEnable)
		{
			pt=new T();
			if(!pt->Init(szInit))
			{
				return FALSE;
			}

			switch(smpiotype)
			{
			case SMIO_TYPE_INOUT:
				RegisterIO(pt);
				break;
			case SMIO_TYPE_IN:
				RegisterInput(pt);
				break;
			case SMIO_TYPE_OUT:
				RegisterOutput(pt);
				break;
			default:
				return FALSE;
			}
		}
		else
		{
			switch(smpiotype)
			{
			case SMIO_TYPE_INOUT:
				UnregisterIO(pt);
				break;
			case SMIO_TYPE_IN:
				UnregisterInput(pt);
				break;
			case SMIO_TYPE_OUT:
				UnregisterOutput(pt);
				break;
			default:
				return FALSE;
			}
		}

		return TRUE;
	}

private:
	void CSmpIO::Unregister(vector<ISmpIO*>* pv, ISmpIO* pIO);

private:
	vector<ISmpIO*> m_vOutIO;
	vector<ISmpIO*> m_vInIO;

private:
	CConsoleIO* m_pConsoleIO;
	CDbgoutIO* m_pDbgoutIO;
	CAtlIO* m_pAtlIO;
	CSocketIO* m_pSocketIO;
	CWindowIO* m_pWindowsIO;
	CCelogIO* m_pCelogIO;
	CSerialIO* m_pSerialIO;
	CFileIO* m_pFileIO;
};

extern CSmpIO g_SmpIO;

#endif	//#ifdef __cplusplus


//
//==== implements ====
//

#if (defined(SMPIO_IMP)|defined(KEN_LIB_IMP))

#ifdef __cplusplus

//
//---- implement of ISmpIO ----
//
int ISmpIO::Printf(const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	return PrintfV(m_dwDeep, NULL, 0, format, arglist);
}

int ISmpIO::Printf(DWORD dwDeep, const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	return PrintfV(dwDeep, NULL, 0, format, arglist);
}

int ISmpIO::Scanf(const TCHAR* format ... )
{
	int ret;

	va_list arglist;
	va_start(arglist, format);

	EnterCriticalSection(&m_cs);
    ret=_input(format, arglist);
	LeaveCriticalSection(&m_cs);

    va_end(arglist);
    
	return ret;
}

void ISmpIO::Trace(const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	PrintfV(0, NULL, 0, format, arglist);
}
void ISmpIO::Trace(DWORD dwDeep, const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	PrintfV(dwDeep, NULL, 0, format, arglist);
}

int ISmpIO::PrintfV(DWORD dwDeep, const TCHAR* pFile, DWORD dwLine, const TCHAR* format, va_list arglist)
{
	int ret;

// reformat add deep
	TCHAR fmtbuf[1024];
	ReFormat(dwDeep, pFile, dwLine, format, fmtbuf);

	TCHAR buf[4096];
	ret=_vsntprintf(buf, ARRAYOF(buf), fmtbuf, arglist);
	
	EnterCriticalSection(&m_cs);
	Puts(buf);
	LeaveCriticalSection(&m_cs);

	return ret;
}

VOID ISmpIO::ReFormat(DWORD dwDeep, const TCHAR* pFile, DWORD dwLine, const TCHAR* format, TCHAR* reformat)
{
	DWORD id;
	for(id=0; id<dwDeep; id++)
	{
		reformat[id]='\t';
	}

	if( pFile!=NULL) 
	{
		id+=_stprintf(reformat, TEXT("file:%s line:%d "), pFile, dwLine);
	}
	else if( m_szFile!=NULL)
	{
		id+=_stprintf(reformat, TEXT("file:%s line:%d "), m_szFile, m_dwLine);
		m_szFile=NULL;
	}

	BOOL fNewLine=FALSE;
	DWORD i;
	for(i=0; i<_tcslen(format); i++)
	{
		if( ('\r'==format[i]) || ('\n'==format[i]) )
		{
			fNewLine=TRUE;
		}
		else
		{
			//after \r\n insert tabs
			if(TRUE==fNewLine)
			{
				for(DWORD j=0; j<dwDeep; j++)
				{
					reformat[id]='\t';
					id++;
				}
				fNewLine=FALSE;
			}
		}

		reformat[id]=format[i];
		id++;
	}
	reformat[id]=0;

/*  use _cputs no append \r\n
	// remove the last \r\n for use puts as out
	if(TRUE==fNewLine)
	{
		if( ('\r'==reformat[id-1]) || ('\n'==reformat[id-1]) )
		{
			reformat[id-1]=0;
		}
		if( ('\r'==reformat[id-2]) || ('\n'==reformat[id-2]) )
		{
			reformat[id-2]=0;
		}
	}
*/
}


//
//---- implement of CFileIO ----
//

BOOL CFileIO::Init(const TCHAR* szInit)
{
	m_hFile=CreateFile(szInit, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE
						, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(INVALID_HANDLE_VALUE==m_hFile)
	{
		return FALSE;
	}

	return TRUE;
}

int CFileIO::Puts(const TCHAR* str)
{
	int ret=0;

	EnterCriticalSection(&m_cs);

	DWORD dwPos=SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
	SetFilePointer(m_hFile, 0, NULL, FILE_END);
	WriteFile(m_hFile, str, _tcslen(str)+1, (DWORD*)&ret, NULL);
	SetFilePointer(m_hFile, dwPos, NULL, FILE_BEGIN);

	LeaveCriticalSection(&m_cs);

	return ret;
}

int CFileIO::Write(BYTE* pbuf, DWORD dwLen)
{
	int ret=0;

	EnterCriticalSection(&m_cs);

	DWORD dwPos=SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
	SetFilePointer(m_hFile, 0, NULL, FILE_END);
	WriteFile(m_hFile, pbuf, dwLen, (DWORD*)&ret, NULL);
	SetFilePointer(m_hFile, dwPos, NULL, FILE_BEGIN);

	LeaveCriticalSection(&m_cs);

	return ret;
}

int CFileIO::Gets(TCHAR* psz, DWORD dwLen)
{
	DWORD 	dwRdRet;
	int		iRet=0;
	TCHAR 	tch;

	EnterCriticalSection(&m_cs);

	for(DWORD i=0; i<dwLen; i++)
	{
		dwRdRet=0;
		ReadFile(m_hFile, &tch, sizeof(tch), &dwRdRet, NULL);

		// the first is \n maybe the last time we not read, ignore it
		if('\n'==tch)
		{
			continue;
		}

		// file end or find return
		if(dwRdRet!=sizeof(tch))
		{
			psz[iRet]=0;
			break;
		}
		
		if('\r'==tch)
		{
			psz[iRet]=0;
			iRet++;
			break;
		}

		psz[iRet]=tch;
		iRet++;
	}

	LeaveCriticalSection(&m_cs);

	return iRet;
}

int CFileIO::Read(BYTE* pbuf, DWORD dwLen)
{
	int ret=0;

	EnterCriticalSection(&m_cs);

	ReadFile(m_hFile, pbuf, dwLen, (DWORD*)&ret, NULL);

	LeaveCriticalSection(&m_cs);

	return ret;
}

//
//---- implement of CSmpIO ----
//
/*
int CConsoleIO::Write(BYTE* pbuf, DWORD dwLen)
{
	DWORD dwWriten=0;
	TCHAR* pTchar=(TCHAR*)pbuf;

	for(DWORD i=0; i<dwLen/sizeof(TCHAR); i++)
	{
		_putw(pTchar[i]);
	}

	return (int)dwLen;
}
*/
//
//---- implement of CSmpIO ----
//
CSmpIO::CSmpIO(SMPIO_CLASS smpiocls)
{
	Init();
	EnBuildInIO(TRUE, smpiocls, SMIO_TYPE_INOUT);
}

VOID CSmpIO::Init()
{
	m_pDbgoutIO=NULL;
	m_pAtlIO=NULL;
	m_pSocketIO=NULL;
	m_pWindowsIO=NULL;
	m_pCelogIO=NULL;
	m_pSerialIO=NULL;
	m_pFileIO=NULL;
}

BOOL CSmpIO::EnBuildInIO(BOOL fEnable, SMPIO_CLASS smpiocls, SMPIO_TYPE smpiotype, const TCHAR* szInit)
{
	switch(smpiocls)
	{
	case SMPIO_CONSOLE:
		return EnBuildInIO(fEnable, m_pConsoleIO, smpiotype, szInit);

	case SMPIO_DBGOUT:
		return EnBuildInIO(fEnable, m_pDbgoutIO, smpiotype, szInit);

	case SMPIO_ATL:
		return EnBuildInIO(fEnable, m_pAtlIO, smpiotype, szInit);

	case SMPIO_SOCKET:
		return EnBuildInIO(fEnable, m_pSocketIO, smpiotype, szInit);

	case SMPIO_WINDOWS:
		return EnBuildInIO(fEnable, m_pWindowsIO, smpiotype, szInit);

	case SMPIO_CELOG:
		return EnBuildInIO(fEnable, m_pCelogIO, smpiotype, szInit);

	case SMPIO_SERIAL:
		return EnBuildInIO(fEnable, m_pSerialIO, smpiotype, szInit);

	case SMPIO_FILE:
		return EnBuildInIO(fEnable, m_pFileIO, smpiotype, szInit);

	default:
		return FALSE;

	}
}

int CSmpIO::Puts(const TCHAR* str)
{
	for_each(m_vOutIO.rbegin(), m_vOutIO.rend(), bind2nd(mem_fun(&ISmpIO::Puts),str));
	return 1;
}

int CSmpIO::Write(BYTE* pbuf, DWORD dwLen)
{
	for(vector<ISmpIO*>::reverse_iterator it=m_vOutIO.rbegin(); it!=m_vOutIO.rend(); it++)
	{
		(*it)->Write(pbuf, dwLen);
	}
	
	return (int)dwLen;
}

int CSmpIO::Gets(TCHAR* psz, DWORD dwLen)
{
	if(0==m_vInIO.size())
	{
		return 0;
	}
	return m_vInIO.back()->Gets(psz, dwLen);
}

int CSmpIO::Read(BYTE* pbuf, DWORD dwLen)
{
	if(0==m_vInIO.size())
	{
		return 0;
	}
	return m_vInIO.back()->Read(pbuf, dwLen);
}

int CSmpIO::_input (const TCHAR* format, va_list arglist )
{
	if(0==m_vInIO.size())
	{
		return 0;
	}
	return m_vInIO.back()->_input(format, arglist);
}

void CSmpIO::Unregister(vector<ISmpIO*>* pv, ISmpIO* pIO)
{
	vector<ISmpIO*>::iterator it=find(pv->begin(), pv->end(), pIO);
	if(it!=pv->end())
	{
		pv->erase(it);
	}
}

//
//---- a system default smpio
//
#if defined(DEF_SMPIO_DBGOUT)
CSmpIO g_SmpIO(SMPIO_DBGOUT);
#elif defined(DEF_SMPIO_ATL)
CSmpIO g_SmpIO(SMPIO_ATL);
#else
CSmpIO g_SmpIO(SMPIO_CONSOLE);
#endif


int SmpIOPrintf(const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	return g_SmpIO.PrintfV(0, NULL, 0, format, arglist);
}
int SmpIODeepPrintf(DWORD dwDeep, const TCHAR* format ...)
{
	va_list arglist;
	va_start(arglist, format);
	return g_SmpIO.PrintfV(dwDeep, NULL, 0, format, arglist);
}

int SmpIOPuts(const TCHAR* str)
{
	return g_SmpIO.Puts(str);
}

int SmpIOWrite(BYTE* pbuf, DWORD dwLen)
{
	return g_SmpIO.Write(pbuf, dwLen);
}

int SmpIOTRACE(DWORD dwDeep, const TCHAR* pszFile, int iLine, const TCHAR* format ...)
{
// reformat add deep
	va_list arglist;
	va_start(arglist, format);
	return g_SmpIO.PrintfV(dwDeep, pszFile, iLine, format, arglist);
}

#endif	//#ifdef __cplusplus


#endif	//#if (defined(SMPIO_IMP)|defined(KEN_LIB_IMP))


#pragma warning( pop ) //#pragma warning(disable : 4996)

#endif	//#ifndef __SMPIO_H__
